import './index.css'
import * as THREE from 'three'
import {
    OrbitControls
} from 'three/examples/jsm/controls/OrbitControls'
import * as dat from 'dat.gui'
import * as CANNON from 'cannon-es'
import CannonDebugger from 'cannon-es-debugger'


/**
 * Size
 */
const Size = {
    width: window.innerWidth,
    height: window.innerHeight
}


/**
 * Camera
 */
const camera = new THREE.PerspectiveCamera(75, Size.width / Size.height, 0.1, 100)
camera.position.set(0, 4, 15)

/**
 * Scene
 */
const scene = new THREE.Scene()


/**
 * Material
 */
const material = new THREE.MeshStandardMaterial({
    color: 0xffffff
})


/**
 * Objects
 */
const planeGoemetry = new THREE.PlaneGeometry(20, 20)
const plane = new THREE.Mesh(planeGoemetry, material)
plane.rotation.set(-Math.PI / 2, 0, 0)
plane.receiveShadow = true


scene.add(plane)


/**
 * Lights
 */
const ambientLight = new THREE.AmbientLight('#ffffff', 0.5)
const directionalLight = new THREE.DirectionalLight('#ffffff', 3)
directionalLight.position.set(-5, 6, 0)
directionalLight.castShadow = true
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight)
scene.add(ambientLight, directionalLight, directionalLightHelper)


/**
 * Physics
 */
const world = new CANNON.World()
world.gravity.set(0, -9.82, 0)
world.allowSleep = true  //性能优化，当几乎不在动时，停止检测

/**
 * 摩擦力及弹性
 */
const defaultMaterial = new CANNON.Material("default")
const defaultContactMaterial = new CANNON.ContactMaterial(defaultMaterial, defaultMaterial, {
    friction: 0.1,
    restitution: 0.6
})
world.addContactMaterial(defaultContactMaterial)



const floorShape = new CANNON.Plane()
const floorBody = new CANNON.Body({
    mass: 0, //固定
    shape: floorShape,
    material: defaultMaterial
})
// 跟平面一样 旋转
//new CANNON.Vec3(1, 0, 0)旋转向量     -Math.PI / 2 角度
floorBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2)
world.addBody(floorBody)


/**
 * Renderer
 */
const canvas = document.querySelector('canvas.webgl')
const renderer = new THREE.WebGLRenderer({
    canvas
})
renderer.setSize(Size.width, Size.height)
renderer.shadowMap.enabled = true


/**
 * OrbitControls
 */
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true


/**
 * createSphere 
 */
const ObjectsToUpdate = []
const sphereGeometry = new THREE.SphereGeometry(1, 16, 16)
const createSphere = (radius, position) => {
    // THREE MESH
    const mesh = new THREE.Mesh(
        sphereGeometry,
        material
    )
    mesh.scale.set(radius, radius, radius)
    mesh.castShadow = true
    mesh.position.copy(position)
    scene.add(mesh)

    //CANNON
    const shape = new CANNON.Sphere(radius)
    const meshBody = new CANNON.Body({
        mass: 1,
        position: position,
        shape: shape,
        material: defaultMaterial
    })
    world.addBody(meshBody)

    ObjectsToUpdate.push({
        mesh: mesh,
        body: meshBody
    })
}
/**
 * createBox
 */
const createBox = (width,height,depth, position) => {
    // THREE MESH
    const mesh = new THREE.Mesh(
        new THREE.BoxGeometry(width,height,depth),
        material
    )
    mesh.castShadow = true
    mesh.position.copy(position)
    scene.add(mesh)

    //CANNON
    const shape = new CANNON.Box(new CANNON.Vec3(width * 0.5, height * 0.5, depth * 0.5))
    const meshBody = new CANNON.Body({
        mass: 1,
        position: position,
        shape: shape,
        material: defaultMaterial
    })
    world.addBody(meshBody)

    ObjectsToUpdate.push({
        mesh: mesh,
        body: meshBody
    })
}




/**
 * CANNON Debug
 */
const cannonMeshes = []
const cannonDebug = CannonDebugger(scene, world, {
    onInit(body, mesh) {
        mesh.visible = true
        cannonMeshes.push(mesh)
    }
})


/**
 * Debug ui
 */
const guiObj = {
    CannonDebugger: false,
    createSphere: ()=> createSphere(Math.random(),new THREE.Vector3((Math.random() - 0.5) * 8, 5, (Math.random() - 0.5) * 8)),
    createBox:()=>createBox(Math.random(),Math.random(),Math.random(),new THREE.Vector3((Math.random() - 0.5) * 8, 5, (Math.random() - 0.5) * 8))
}
const gui = new dat.GUI();
gui.add(guiObj, 'CannonDebugger').name('CannonDebugger mesh visible').onChange((value) => {
    if (value) {
        cannonMeshes.forEach((item) => {
            item.visible = true
        })
    } else {
        cannonMeshes.forEach((item) => {
            item.visible = false
        })
    }
})
gui.add(guiObj,'createSphere')
gui.add(guiObj,'createBox')


/**
 * Update
 */

const tick = () => {
    // cannonDebug.update()
    controls.update();
    /**
     * Physics
     */
    world.fixedStep()

    ObjectsToUpdate.forEach(object => {
        object.mesh.position.copy(object.body.position)
        // 旋转
        object.mesh.quaternion.copy(object.body.quaternion)
    })

    renderer.render(scene, camera)
    requestAnimationFrame(tick)
}
tick()